package com.dqx.opengl_es_demo.cube;

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * 正方体渲染器（正方体是由六个正方形组成的
 * 而每个正方形又由2个三角形组成；正方体拥有八个顶点；
 * 这里采用索引法绘制正方体）
 */
public class CubeRenderer implements GLSurfaceView.Renderer {

    //顶点缓冲区对象
    private FloatBuffer vertexBuffer;
    //颜色缓冲区对象
    private FloatBuffer colorBuffer;
    //索引缓冲区对象
    private ShortBuffer indexBuffer;
    //顶点渲染器
    private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "uniform mat4 vMatrix;" +
                    "varying  vec4 vColor;" +
                    "attribute vec4 aColor;" +
                    "void main() {" +
                    "  gl_Position = vMatrix*vPosition;" +
                    "  vColor=aColor;" +
                    "}";
    //片段渲染器
    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "varying vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";
    //程序id
    private int program;
    //顶点着色器
    int vertexShader;
    //创建片元着色器
    int fragmentShader;
    //浮点型占4个字节长度
    final int FLOAT_BYTE_LENGTH = 4;
    //顶点坐标
    final float cubePositions[] = {
            -1.0f, 1.0f, 1.0f,    //正面左上0
            -1.0f, -1.0f, 1.0f,   //正面左下1
            1.0f, -1.0f, 1.0f,    //正面右下2
            1.0f, 1.0f, 1.0f,     //正面右上3
            -1.0f, 1.0f, -1.0f,    //反面左上4
            -1.0f, -1.0f, -1.0f,   //反面左下5
            1.0f, -1.0f, -1.0f,    //反面右下6
            1.0f, 1.0f, -1.0f,     //反面右上7
    };
    //索引数组
    final short index[] = {
            6, 7, 4, 6, 4, 5,    //后面
            6, 3, 7, 6, 2, 3,    //右面
            6, 5, 1, 6, 1, 2,    //下面
            0, 3, 2, 0, 2, 1,    //正面
            0, 1, 5, 0, 5, 4,    //左面
            0, 7, 3, 0, 4, 7,    //上面
    };
    //颜色数组
    float color[] = {
            0f, 1f, 0f, 1f,
            0f, 1f, 0f, 1f,
            0f, 1f, 0f, 1f,
            0f, 1f, 0f, 1f,
            1f, 0f, 0f, 1f,
            1f, 0f, 0f, 1f,
            1f, 0f, 0f, 1f,
            1f, 0f, 0f, 1f,
    };
    //顶点位置属性句柄
    private int mPositionHandle;
    //颜色属性句柄
    private int mColorHandle;
    //相机矩阵
    private float[] mViewMatrix = new float[16];
    //投影矩阵
    private float[] mProjectMatrix = new float[16];
    //变化矩阵
    private float[] mMVPMatrix = new float[16];
    //变化矩阵句柄
    private int mMatrixHandler;

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //设置背景（RGBA）
        GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        //开启深度测试（若不使用深度测试，当我们先绘制一个距离较近的物体，再绘制距离较远的物体
        // ，则距离远的物体因为后绘制，会把距离近的物体覆盖掉，这样的效果并不是我们所希望的。
        // 有了深度缓冲以后，绘制物体的顺序就不那么重要了，都能按照远近（Z值）正常显示）
        GLES20.glEnable(GLES20.GL_DEPTH_TEST);

        //初始化缓冲区对象
        vertexBuffer = initFloatBuffer(cubePositions);
        colorBuffer = initFloatBuffer(color);
        indexBuffer = initShortBuffer(index);
        //创建程序
        program = creatProgram();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //计算宽高比
        float ratio = (float) width / height;
        //设置透视投影
        Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 20);
        //设置相机位置
        Matrix.setLookAtM(mViewMatrix, 0, 5.0f, 5.0f, 10.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        //计算变换矩阵
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mViewMatrix, 0);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        //清除深度缓存和颜色缓存
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        //绘制
        draw();
    }

    /**
     * 初始化缓冲区对象
     *
     * @param buffer
     * @return
     */
    private FloatBuffer initFloatBuffer(float[] buffer) {
        FloatBuffer fb = ByteBuffer.allocateDirect(buffer.length * FLOAT_BYTE_LENGTH)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(buffer);
        fb.position(0);
        return fb;
    }

    /**
     * 初始化缓冲区对象
     *
     * @param buffer
     * @return
     */
    private ShortBuffer initShortBuffer(short[] buffer) {
        ShortBuffer fb = ByteBuffer.allocateDirect(buffer.length * FLOAT_BYTE_LENGTH)
                .order(ByteOrder.nativeOrder())
                .asShortBuffer()
                .put(buffer);
        fb.position(0);
        return fb;
    }

    /**
     * 创建着色器，并返回着色器对象id
     *
     * @param type       着色器类型
     * @param shaderCode 着色器代码
     * @return
     */
    private int loadShader(int type, String shaderCode) {
        //创建着色器对象
        int shader = GLES20.glCreateShader(type);
        //加载着色器代码源
        GLES20.glShaderSource(shader, shaderCode);
        //编译着色器
        GLES20.glCompileShader(shader);
        return shader;
    }

    /**
     * 创建程序
     */
    private int creatProgram() {
        //程序对象id
        int program = 0;
        //创建顶点着色器
        vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        //创建片元着色器
        fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
        //创建程序对象
        program = GLES20.glCreateProgram();
        //将顶点着色器加到程序中
        GLES20.glAttachShader(program, vertexShader);
        //将片段着色器加到程序中
        GLES20.glAttachShader(program, fragmentShader);
        //连接着色器
        GLES20.glLinkProgram(program);
        return program;
    }

    /**
     * 绘制三角形
     */
    public void draw() {
        //将程序加入到OpenGLES2.0环境
        GLES20.glUseProgram(program);
        //获取变换矩阵vMatrix成员句柄
        mMatrixHandler = GLES20.glGetUniformLocation(program, "vMatrix");
        //设置vMatrix的值
        GLES20.glUniformMatrix4fv(mMatrixHandler, 1, false, mMVPMatrix, 0);
        //获取顶点位置属性句柄
        mPositionHandle = GLES20.glGetAttribLocation(program, "vPosition");
        //启用顶点位置属性的句柄
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //设置顶点坐标数据
        GLES20.glVertexAttribPointer(mPositionHandle, 3,
                GLES20.GL_FLOAT, false,
                0, vertexBuffer);
        //获取颜色属性的句柄
        mColorHandle = GLES20.glGetAttribLocation(program, "aColor");
        //启用颜色属性的句柄
        GLES20.glEnableVertexAttribArray(mColorHandle);
        //设置颜色值
        GLES20.glVertexAttribPointer(mColorHandle, 4, GLES20.GL_FLOAT, false, 0, colorBuffer);
        //索引法绘制正方体
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, index.length, GLES20.GL_UNSIGNED_SHORT, indexBuffer);
        //禁止顶点数组的句柄
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }

    /**
     * 释放着色器资源
     */
    private void relese() {
        GLES20.glDeleteShader(vertexShader);
        GLES20.glDeleteShader(fragmentShader);
        GLES20.glDeleteProgram(program);
    }
}
